#include "engine_impl.hh"
#include "path_pool.hh"
#include "command_option.hh"
#include "../corelib/grid/path.hh"
#include "../corelib/grid/site.hh"
#include "../corelib/util/string_util.hh"
#include "../corelib/network/mem_pool.hh"
#include "../corelib/util/guid.hh"

namespace kratos { namespace engine {

bool EngineImpl::findPath(SiteType siteType, UserData userData) {
    ENGINE_PARAM_FAIL_RETURN_FALSE(!siteType);
    //auto pathID = pathPool_->getPath(siteType, userData);
    //if (pathID != INVALID_PATH_ID) {
    //    // call service's callback
    //    auto& modules = moduleManager_.getTypicalModules(ModuleType::EVENT_MODULE);
    //    for (auto modulePtr : modules) {
    //        auto service = modulePtr->getService();
    //        if (service) {
    //            try {
    //                service->onPathJoin(pathID, siteType, userData);
    //            } catch (std::exception& e) {
    //                ENGINE_LOG_EXCEPTION(e, "Service::onPathJoin, pathID:", pathID);
    //            }
    //        }
    //    }
    //}
    //return true;
    return internalFindPath(siteType, userData);
}

const grid::SiteID& EngineImpl::getPathPeerSiteID(PathID pathID) {
    auto& path = grid_->pathManager().getPath(pathID);
    if (!path) {
        return grid::NullSiteID;
    }
    return path.getPeerSiteID();
}

void EngineImpl::closePath(PathID pathID) {
    grid_->pathManager().close(pathID);
}

void EngineImpl::closeSession(SessionID sessionID) {
    auto& session = grid_->getSession(sessionID);
    if (session) {
        session.close();
    }
}

bool EngineImpl::talkPath(PathID pathID, const char * data, std::size_t size) {
    ENGINE_PARAM_FAIL_RETURN_FALSE(!pathID);
    ENGINE_PARAM_FAIL_RETURN_FALSE(!data);
    ENGINE_PARAM_FAIL_RETURN_FALSE(!size);
    auto& path = grid_->pathManager().getPath(pathID);
    if (path) {
        path.talk(data, size);
        return true;
    }
    return false;
}

bool EngineImpl::talkSession(SessionID sessionID, const char * data, std::size_t size) {
    ENGINE_PARAM_FAIL_RETURN_FALSE(!sessionID);
    ENGINE_PARAM_FAIL_RETURN_FALSE(!data);
    ENGINE_PARAM_FAIL_RETURN_FALSE(!size);
    auto& session = grid_->getSession(sessionID);
    if (session) {
        session.getWriter().talk(data, size);
        return true;
    }
    return false;
}

bool EngineImpl::talkSite(const grid::SiteID & siteID, const char * data, std::size_t size) {
    ENGINE_PARAM_FAIL_RETURN_FALSE(!data);
    ENGINE_PARAM_FAIL_RETURN_FALSE(!size);
    return grid_->talk(siteID, data, size);
}

bool EngineImpl::broadcastLoactor(const char * data, std::size_t size) {
    if (locatorPathes_.empty()) {
        return false;
    }
    for (auto pathID : locatorPathes_) {
        talkPath(pathID, data, size);
    }
    return true;
}

PathID EngineImpl::getLocator(std::uint64_t entityID) {
    auto value = hashing_.getNode(entityID, hashType_);
    if (!value) {
        return INVALID_PATH_ID;
    }
    return value;
}

PathID EngineImpl::getEntityPathID(std::uint64_t entityID) {
    auto it = locatorEntityMap_.find(entityID);
    if (it == locatorEntityMap_.end()) {
        return INVALID_PATH_ID;
    }
    return it->second.pathID;
}

bool EngineImpl::addEntity(PathID pathID, std::uint64_t entityID, std::uint32_t entityType) {
    auto it = locatorEntityMap_.find(entityID);
    if (it != locatorEntityMap_.end()) {
        return false;
    }
    locatorEntityMap_[entityID] = EntityInfo{ entityID, pathID, entityType };
    locatorEntityTypeMap_[entityType].push_back(entityID);
    return true;
}

std::uint32_t EngineImpl::getEntityType(std::uint64_t entityID) {
    auto it = locatorEntityMap_.find(entityID);
    if (it == locatorEntityMap_.end()) {
        return 0;
    }
    return it->second.entityType;
}

const EntityIDQueue& EngineImpl::getEntities(std::uint32_t entityType) {
    return locatorEntityTypeMap_[entityType];
}

bool EngineImpl::removeEntity(PathID pathID, std::uint64_t entityID) {
    auto it = locatorEntityMap_.find(entityID);
    if (it == locatorEntityMap_.end()) {
        return false;
    }
    auto entityType = it->second.entityType;
    auto& entityIDs = locatorEntityTypeMap_[entityType];
    entityIDs.erase(std::remove(entityIDs.begin(), entityIDs.end(), entityID), entityIDs.end());
    locatorEntityMap_.erase(it);
    return true;
}

TimerHandle EngineImpl::startTimer(std::time_t duration) {
    return timerQueue_.startTimer(duration, now_);
}

TimerHandle EngineImpl::startTimerOnce(std::time_t duration) {
    return timerQueue_.startTimerOnce(duration, now_);
}

void EngineImpl::cancelTimer(TimerHandle timerID) {
    timerQueue_.cancel(timerID);
}

ProtocolAnalyzer * EngineImpl::getProtocolAnalyzer() {
    return &protocolAnalyzer_;
}

kconfig::Config * EngineImpl::getConfig() {
    return config_;
}

corelib::Log & EngineImpl::getLogverb() {
    if (CommandOptionsRef.getServiceName().empty()) {
        return logverb;
    } else {
        return logverb << "[" << CommandOptionsRef.getServiceName() << "]";
    }
}

corelib::Log & EngineImpl::getLogdebug() {
    if (CommandOptionsRef.getServiceName().empty()) {
        return logdebug;
    } else {
        return logdebug << "[" << CommandOptionsRef.getServiceName() << "]";
    }
}

corelib::Log & EngineImpl::getLogwarn() {
    if (CommandOptionsRef.getServiceName().empty()) {
        return logwarn;
    } else {
        return logwarn << "[" << CommandOptionsRef.getServiceName() << "]";
    }
}

corelib::Log & EngineImpl::getLogerror() {
    if (CommandOptionsRef.getServiceName().empty()) {
        return logerror;
    } else {
        return logerror << "[" << CommandOptionsRef.getServiceName() << "]";
    }
}

corelib::Log & EngineImpl::getLogfatal() {
    if (CommandOptionsRef.getServiceName().empty()) {
        return logfatal;
    } else {
        return logfatal << "[" << CommandOptionsRef.getServiceName() << "]";
    }
}

const corelib::Finish & EngineImpl::getLogend() {
    return logend;
}

khttp::HttpRequest * EngineImpl::newHttpRequest(khttp::HttpCallback * cb, const std::string& host,
    const std::string& uri, int32_t port) {
    ENGINE_PARAM_FAIL_RETURN(!cb, nullptr);
    ENGINE_PARAM_FAIL_RETURN(host.empty(), nullptr);
    auto request = http_->newRequest(cb, 10);
    ENGINE_PARAM_FAIL_RETURN(!request, nullptr);
    std::string realHost(host);
    if (corelib::StringUtil::startWith(host, "http://")) {
        realHost = corelib::StringUtil::remove(host, "http://");
    }
    request->setHost(realHost);
    request->setPort(port);
    request->setUri(uri);
    return request;
}

bool EngineImpl::newHttpServer(khttp::HttpCallback* cb, const std::string& host, int32_t port) {
    ENGINE_PARAM_FAIL_RETURN_FALSE(!cb);
    ENGINE_PARAM_FAIL_RETURN_FALSE(host.empty());
    std::string realHost(host);
    if (corelib::StringUtil::startWith(host, "http://")) {
        realHost = corelib::StringUtil::remove(host, "http://");
    }
    return http_->listenAt(realHost, port, cb, 10);
}

std::string EngineImpl::doCommand(const std::string & moduleName, const std::string & command) {
    auto module = moduleManager_.getModule(moduleName);
    if (!module) {
        return "Module not found";
    }
    return module->getService()->execute(command);
}

std::string EngineImpl::doCommand(const std::string & command) {
    return "Command not found";
}

ObjectPoolManager & EngineImpl::getObjectPoolManager() {
    return ObjectPoolManagerRef;
}

ComponentManager& EngineImpl::getComponentManager() {
    return componentManager_;
}

std::size_t EngineImpl::getMemoryPoolSize() {
    return MempoolRef.getTotalHeapSize();
}

const std::string& EngineImpl::getPluginRootPath(const std::string& name) {
    return moduleManager_.getServiceRootPath(name);
}

std::uint64_t EngineImpl::getUUID() {
    return corelib::Guid::generate(grid_->property().type, grid_->property().host);
}

std::uint64_t EngineImpl::getUUIDRandom() {
    return corelib::Guid::generateRandom(grid_->property().type, grid_->property().host);
}

bool EngineImpl::isRouter() {
    return grid_->property().isRouter;
}

bool EngineImpl::connect(const std::string& host, std::uint64_t userData, std::time_t timeout) {
    return false;
}

}}
